
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using WpfPropertyEditorExtension;

namespace WpfXamlPropertyGridVSIX
{
    // Dynamic wrapper: exposes all attributes as editable properties in PropertyGrid.
    public class XElementWrapper : ICustomTypeDescriptor
    {
        private readonly XElement _element;
        private readonly XamlEditorService _svc;

        public XElementWrapper(XElement element, XamlEditorService svc)
        {
            _element = element;
            _svc = svc;
        }

        [Category("Element")]
        [ReadOnly(true)]
        [DisplayName("ElementName")]
        public string ElementName => _element.Name.ToString();

        [Category("Element")]
        [ReadOnly(true)]
        [DisplayName("StartLine")]
        public int StartLine => (_element as IXmlLineInfo)?.LineNumber ?? 0;

        public AttributeCollection GetAttributes() => AttributeCollection.Empty;
        public string GetClassName() => _element.Name.LocalName;
        public string GetComponentName() => _element.Name.LocalName;
        public TypeConverter GetConverter() => null;
        public EventDescriptor GetDefaultEvent() => null;
        public PropertyDescriptor GetDefaultProperty() => null;
        public object GetEditor(Type editorBaseType) => null;
        public EventDescriptorCollection GetEvents() => EventDescriptorCollection.Empty;
        public EventDescriptorCollection GetEvents(Attribute[] attributes) => EventDescriptorCollection.Empty;

        public PropertyDescriptorCollection GetProperties() => GetProperties(null);

        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            var list = new List<PropertyDescriptor>();

            // Add attributes (sorted by name)
            foreach (var attr in _element.Attributes().OrderBy(a => a.Name.LocalName))
            {
                list.Add(new XElementPropertyDescriptor(_element, attr, _svc));
            }

            // Add pseudo-property for inner text if element has no child elements.
            if (!_element.HasElements)
            {
                list.Add(new XElementInnerTextDescriptor(_element, _svc));
            }

            return new PropertyDescriptorCollection(list.ToArray());
        }

        public object GetPropertyOwner(PropertyDescriptor pd) => this;
    }

    internal class XElementInnerTextDescriptor : PropertyDescriptor
    {
        private readonly XElement _el;
        private readonly XamlEditorService _svc;

        public XElementInnerTextDescriptor(XElement el, XamlEditorService svc)
            : base("(InnerText)", null)
        {
            _el = el; _svc = svc;
        }

        public override bool CanResetValue(object component) => true;
        public override Type ComponentType => typeof(XElementWrapper);
        public override object GetValue(object component) => _el.Value;
        public override bool IsReadOnly => false;
        public override Type PropertyType => typeof(string);
        public override void ResetValue(object component) => _el.Value = "";
        public override bool ShouldSerializeValue(object component) => true;
        public override void SetValue(object component, object value)
        {
            _el.Value = value?.ToString() ?? "";
            _svc.UpdateStartTagOrContent(_el, updateContent:true);
        }
    }
}
